package com.zyht.common.web.log;

import java.io.FileNotFoundException;
import java.lang.reflect.Method;

import javax.servlet.ServletContext;

import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.AbstractEnvironment;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.ResourceUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.util.ServletContextPropertyUtils;
import org.springframework.web.util.WebUtils;

import ch.qos.logback.core.joran.spi.JoranException;
import ch.qos.logback.ext.spring.LogbackConfigurer;
import ch.qos.logback.ext.spring.web.WebLogbackConfigurer;

class WebLogbackProfileConfigurer {

	private WebLogbackProfileConfigurer(){
	}

	public static void initLogging(ServletContext servletContext){

		// Expose the web app root system property.
		if(exposeWebAppRoot(servletContext)){
			WebUtils.setWebAppRootSystemProperty(servletContext);
		}
		
		String profileParam = servletContext.getInitParameter(AbstractEnvironment.DEFAULT_PROFILES_PROPERTY_NAME);
		// Only perform custom Logback initialization in case of a config file.
		String locationParam = servletContext.getInitParameter(WebLogbackConfigurer.CONFIG_LOCATION_PARAM);
		if(locationParam != null){
			// Perform Logback initialization; else rely on Logback's default initialization.
			for(String location:StringUtils.tokenizeToStringArray(locationParam,ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS)){
				try{
					if(null!=profileParam){
						location = location.replace(".xml", "-"+profileParam + ".xml");
					}
					// Resolve context property placeholders before potentially resolving real path.
					location = ServletContextPropertyUtils.resolvePlaceholders(location,servletContext);
					// Return a URL (e.g. "classpath:" or "file:") as-is;
					// consider a plain file path as relative to the web application root directory.
					if(!ResourceUtils.isUrl(location)){
						location = WebUtils.getRealPath(servletContext,location);
					}

					// Write log message to server log.
					servletContext.log("Initializing Logback from [" + location + "]");

					// Initialize
					LogbackConfigurer.initLogging(location);
					break;
				} catch (FileNotFoundException ex){
					servletContext.log("No logback configuration file found at [" + location + "]");
					// throw new IllegalArgumentException("Invalid 'logbackConfigLocation' parameter: " +
					// ex.getMessage());
				} catch (JoranException e){
					throw new RuntimeException("Unexpected error while configuring logback",e);
				}
			}
		}

		// If SLF4J's java.util.logging bridge is available in the classpath, install it. This will direct any messages
		// from the Java Logging framework into SLF4J. When logging is terminated, the bridge will need to be
		// uninstalled
		try{
			Class<?> julBridge = ClassUtils.forName("org.slf4j.bridge.SLF4JBridgeHandler",ClassUtils.getDefaultClassLoader());

			Method removeHandlers = ReflectionUtils.findMethod(julBridge,"removeHandlersForRootLogger");
			if(removeHandlers != null){
				servletContext.log("Removing all previous handlers for JUL to SLF4J bridge");
				ReflectionUtils.invokeMethod(removeHandlers,null);
			}

			Method install = ReflectionUtils.findMethod(julBridge,"install");
			if(install != null){
				servletContext.log("Installing JUL to SLF4J bridge");
				ReflectionUtils.invokeMethod(install,null);
			}
		} catch (ClassNotFoundException ignored){
			// Indicates the java.util.logging bridge is not in the classpath. This is not an indication of a problem.
			servletContext.log("JUL to SLF4J bridge is not available on the classpath");
		}
	}

	/**
	 * Return whether to expose the web app root system property, checking the corresponding ServletContext init
	 * parameter.
	 *
	 * @param servletContext
	 *            the servlet context
	 * @return {@code true} if the webapp's root should be exposed; otherwise, {@code false}
	 * @see #EXPOSE_WEB_APP_ROOT_PARAM
	 */
	private static boolean exposeWebAppRoot(ServletContext servletContext){

		String exposeWebAppRootParam = servletContext.getInitParameter(WebLogbackConfigurer.EXPOSE_WEB_APP_ROOT_PARAM);
		return (exposeWebAppRootParam == null || Boolean.valueOf(exposeWebAppRootParam));
	}
}
